package main

import (
	"fmt"
	"io"
	"net/http"
	"net/rpc"
	"net/rpc/jsonrpc"

	"gitee.com/go-course/grpc-demo/rpc/service"
)

// var
// var var1 int = "string"
// var _ Service = &HelloService{}
// int(60)  --> (int)(<type>)
// 我们声明了一个空指针, 强制把这个指针转换成了一个*HelloService
var _ service.Service = (*HelloService)(nil)

type HelloService struct{}

// 业务场景
// 该函数需要被客户端调用
// name ---->
// <---- hello, name
// 改成成符合rpc规范的 函数签名
// 1. 第一个参数 request, interface{}
// 2. 第二参数是一个响应 reponse, interface{}, 必须是一个指针
// 3. 返回一个error
func (s *HelloService) Hello(name string, resp *string) error {
	*resp = fmt.Sprintf("Hello, %s", name)
	return nil
}

//  TCP RPC
// func main() {
// 	// 发要提供的服务注册给RPC框架
// 	err := rpc.RegisterName(service.Name, new(HelloService))
// 	if err != nil {
// 		panic(err)
// 	}

// 	// 监听Socket
// 	listener, err := net.Listen("tcp", ":1234")
// 	if err != nil {
// 		panic(err)
// 	}

// 	for {
// 		conn, err := listener.Accept()
// 		if err != nil {
// 			panic(err)
// 		}

// 		// 前面都是tcp的知识, 到这个RPC就接管了
// 		// 因此 你可以认为 rpc 帮我们封装消息到函数调用的这个逻辑,
// 		// 提升了工作效率, 逻辑比较简洁，可以看看他代码
// 		// 实现 服务端的JSON编解码
// 		go rpc.ServeCodec(jsonrpc.NewServerCodec(conn))
// 	}
// }

func NewRPCReadWriteCloserFromHTTP(w http.ResponseWriter, r *http.Request) *RPCReadWriteCloser {
	return &RPCReadWriteCloser{w, r.Body}
}

type RPCReadWriteCloser struct {
	io.Writer
	io.ReadCloser
}

// HTTP RPC
func main() {
	// 发要提供的服务注册给RPC框架
	err := rpc.RegisterName(service.Name, new(HelloService))
	if err != nil {
		panic(err)
	}

	// RPC的服务架设在“/jsonrpc”路径，
	// 在处理函数中基于http.ResponseWriter和http.Request类型的参数构造一个io.ReadWriteCloser类型的conn通道。
	// 然后基于conn构建针对服务端的json编码解码器。
	// 最后通过rpc.ServeRequest函数为每次请求处理一次RPC方法调用
	http.HandleFunc("/jsonrpc", func(w http.ResponseWriter, r *http.Request) {
		// 如何把 HTTP的请求 --->  RPC的请求?
		// 基于HTTP Request Body和 Response构造了一个Conn
		conn := NewRPCReadWriteCloserFromHTTP(w, r)
		rpc.ServeRequest(jsonrpc.NewServerCodec(conn))
	})

	http.ListenAndServe(":1234", nil)
}
